#include <iostream>

#include "SharedMemory.hpp"

#ifdef SHARED_MEMORY_HEADER_ONLY
#define SM__FN inline
#else
#define SM__FN
#endif

namespace vrts
{
    SM__FN SharedMemory::SharedMemory(std::string sharedName, size_t memorySize, std::string fileName) :
		m_sharedName(sharedName)
		, m_memorySize(memorySize)
		, m_fileName(fileName)
    {
        m_hMapFile = INVALID_HANDLE_VALUE;
        m_hFile = INVALID_HANDLE_VALUE;
        if (false == CreateMMF())
        {
            std::cout << "Error:createMMF fail!" << std::endl;
            getchar();
            return;
        }
    }

    SM__FN SharedMemory::~SharedMemory()
    {
        if (m_baseAddr)
        {
            UnmapViewOfFile(m_baseAddr);
            m_baseAddr = nullptr;
        }

        if (INVALID_HANDLE_VALUE != m_hMapFile)
        {
            CloseHandle(m_hMapFile);
            m_hMapFile = INVALID_HANDLE_VALUE;
        }

        if (INVALID_HANDLE_VALUE != m_hFile)
        {
            CloseHandle(m_hFile);
            m_hFile = INVALID_HANDLE_VALUE;
        }
    }

    SM__FN bool SharedMemory::CreateMMF()
    {
#if 1
        m_hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, m_sharedName.c_str());
        if (m_hMapFile == nullptr)
        {
            if (m_fileName != "")
            {
                m_hFile = CreateFile(m_fileName.c_str(),
                                     GENERIC_READ | GENERIC_WRITE,
                                     FILE_SHARE_READ | FILE_SHARE_WRITE,
                                     nullptr,
                                     OPEN_ALWAYS,
                                     0,
                                     nullptr);

                if (m_hFile == INVALID_HANDLE_VALUE)
                {
                    return false;
                }
            }

            m_hMapFile = CreateFileMapping(
                m_hFile,               // use paging file
                nullptr,               // default security
                PAGE_READWRITE,        // read/write access
                0,                     // maximum object size (high-order DWORD)
                m_memorySize,          // maximum object size (low-order DWORD)
                m_sharedName.c_str()); // name of mapping object
        }

        if (m_hMapFile == nullptr)
        {
            return false;
        }

        m_baseAddr = (unsigned char *)MapViewOfFile(m_hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, m_memorySize);

        if (nullptr == m_baseAddr)
        {
            return false;
        }
#else
        m_hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, m_sharedName.c_str());
        if (m_hMapFile == nullptr)
        {
            m_hMapFile = CreateFileMapping(
                INVALID_HANDLE_VALUE,  // use paging file
                NULL,                  // default security
                PAGE_READWRITE,        // read/write access
                0,                     // maximum object size (high-order DWORD)
                m_memorySize,          // maximum object size (low-order DWORD)
                m_sharedName.c_str()); // name of mapping object
        }
        m_baseAddr = (unsigned char *)MapViewOfFile(m_hMapFile,          // handle to map object
                                                    FILE_MAP_ALL_ACCESS, // read/write permission
                                                    0,
                                                    0,
                                                    m_memorySize);
        if (nullptr == m_baseAddr)
        {
            return false;
        }
#endif

        return true;
    }

    SM__FN unsigned char *SharedMemory::GetBaseAddr()
    {
        return m_baseAddr;
    }

    SM__FN size_t SharedMemory::GetMemorySize()
    {
        return m_memorySize;
    }

    SM__FN bool SharedMemory::WriteMemory(size_t offset, void *data, size_t length)
    {
        if (!m_baseAddr)
        {
            std::cout << "createMMF fail!" << std::endl;
            return false;
        }
        memcpy(m_baseAddr + offset, data, length);
        return true;
    }

    SM__FN bool SharedMemory::ReadMemory(size_t offset, void *data, size_t length)
    {
        if (!m_baseAddr)
        {
            std::cout << "createMMF fail!" << std::endl;
            return false;
        }
        memcpy(data, m_baseAddr + offset, length);
        return true;
    }

    SM__FN void SharedMemory::ClearMemory(size_t offset, size_t length)
    {
        if (!m_baseAddr)
        {
            std::cout << "createMMF fail!" << std::endl;
            return;
        }
        memset(m_baseAddr + offset, 0, length);
    }
} // namespace vrts
